/*******************************************************************************
 * Copyright (c) 2011, 2012 Mia-Software.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Herve ESTEGUET (Mia-Software) - Bug 381352 - [Releng] There must be a project to configure the build by tycho
 *******************************************************************************/
package org.eclipse.releng.tycho.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.URL;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.DirSet;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.resources.FileResource;

public class MetricsBuilderTask extends Task {

	private DirSet plugins;
	private FileSet testReports;
	private File outputDir;
	private String outputFileName = "metrics.xml";
	private String testResultsFileName = "testresults.xml";
	
	
	@Override
	public void execute() throws BuildException {
		checkParams();
		//Creating the test result files
		File testResultFile;
		if (outputDir != null && outputDir.isDirectory()) {
			testResultFile = new File(outputDir, testResultsFileName);
		} else {
			testResultFile = new File(testResultsFileName);
			if (!testResultFile.getParentFile().isDirectory()) {
				testResultFile.getParentFile().mkdirs();
			}
		}
		if (!testResultFile.getParentFile().isDirectory()) {
			throw new BuildException("Unable to compute the output directory", getLocation());
		}
		OutputStream output = null;
		try {
			output = new FileOutputStream(testResultFile);
			log("Writing test results to " + testResultFile.getAbsolutePath());
			generateTestsResultsXML(output);
		} catch (IOException e) {
			log(e.getMessage(), e, 2);
			throw new BuildException("Unable to save test results to " + testResultFile.getAbsolutePath(), e, getLocation()); 
		} finally {
			try {
				output.close();
			} catch(Exception e) {
				//Do nothing
			}
		}
		
		//Creating the report file
		File resultFile;
		if (outputDir != null && outputDir.isDirectory()) {
			resultFile = new File(outputDir, outputFileName);
		} else {
			resultFile = new File(outputFileName);
			if (!resultFile.getParentFile().isDirectory()) {
				resultFile.getParentFile().mkdirs();
			}
		}
		if (!resultFile.getParentFile().isDirectory()) {
			throw new BuildException("Unable to compute the output directory", getLocation());
		}
		try {
			output = new FileOutputStream(resultFile);
			log("Writing project metrics to " + resultFile.getAbsolutePath());
			buildMetricsFile(output);
		} catch (IOException e) {
			log(e.getMessage(), e, 2);
			throw new BuildException("Unable to save metrics to " + resultFile.getAbsolutePath(), e, getLocation()); 
		} finally {
			try {
				output.close();
			} catch(Exception e) {
				//Do nothing
			}
		}
	}

	public void setOutputFileName(final String aFileName) {
		this.outputFileName = aFileName;
	}

	public void setOutputDir(final File aDirectory) {
		this.outputDir = aDirectory;
	}

	public void addConfiguredPlugins(final DirSet aDirSet) {
		this.plugins = aDirSet;
	}
	
	public void addConfiguredTestReports(final FileSet aFileSet) {
		this.testReports = aFileSet;
		this.testReports.getDirectoryScanner().addDefaultExcludes();
	}
	
	private void checkParams() throws BuildException {
		if (plugins == null) {
			throw new BuildException(getTaskName() + " required a nested plugins element.", getLocation());
		}
	}
	
	private int countOpenedBugs() {
		int result = getBugsCount("https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;bug_status=UNCONFIRMED;bug_status=NEW;bug_status=ASSIGNED;bug_status=REOPENED;component=Main;component=Web;product=EMFT.facet;classification=Modeling");
		log("Counting opened bugs ... " + result);
		return result;
	}

	private int countClosedBugs() {
		int result = getBugsCount("https://bugs.eclipse.org/bugs/buglist.cgi?query_format=advanced;bug_status=RESOLVED;bug_status=VERIFIED;bug_status=CLOSED;product=EMFT.facet;classification=Modeling");
		log("Counting resolved bugs ... " + result);
		return result;
	}

	private int getBugsCount(final String urlStr) {
		int result = -1;
		try {
			URL url = new URL(urlStr);
			InputStream inputStream = url.openStream();
			try {
				result = getBugsCountFromStream(inputStream);
			} finally {
				if (inputStream != null) {
					inputStream.close();
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	private int getBugsCountFromStream(final InputStream inputStream) throws IOException {
		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		String line;
		Pattern pattern = Pattern.compile("[^\\d]*(\\d+)\\s*bugs\\s+found.*", Pattern.DOTALL);
		int result = -1;
		while ((line = reader.readLine()) != null) {
			Matcher matcher = pattern.matcher(line);
			if (matcher.matches()) {
				result = Integer.valueOf(matcher.group(1));
				break;
			}
		}
		return result;
	}

	private int getPluginsCount() {
		int result = 0;
		for (Iterator<?> it = this.plugins.iterator() ; it.hasNext() ;) {
			FileResource file = (FileResource) it.next();
			if (file.isDirectory() && new File(file.getFile(), "plugin.xml").isFile()) {
				result++;
			}
		}
		log("Counting the number of plugins ... " + result);
		return result;
	}
	
	private void buildMetricsFile(final OutputStream writer) throws IOException {
		PrintStream out = new PrintStream(writer);
		out.println("<?xml version=\"1.0\"?>");
		out.println("<metrics>");
		out.printf("\t<plugins count=\"%d\" />\n", getPluginsCount());
		out.printf("\t<bugs open=\"%d\" closed=\"%d\" />\n", countOpenedBugs(), countClosedBugs());
		out.println("</metrics>");
	}
	
	private void generateTestsResultsXML(final OutputStream writer) {
		PrintStream out = new PrintStream(writer);
		out.println("<?xml version=\"1.0\"?>");
		out.println("<testresults>");
		if (this.testReports != null) {
			for (Iterator<?> it = this.testReports.iterator(); it.hasNext();) {
				FileResource file = (FileResource) it.next();
				if (file.getName().endsWith(".xml")) {
					out.printf("\t<testresult>%s</testresult>\n", file.getFile().getAbsolutePath());
				}
			}
		}
		out.println("</testresults>");
	}
	
}
